/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/*
 * Copyright (c) 2010,2011,2012,2013 TELEMATICS LAB, Politecnico di Bari
 *
 * This file is part of LTE-Sim
 *
 * LTE-Sim is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 3 as
 * published by the Free Software Foundation;
 *
 * LTE-Sim is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with LTE-Sim; if not, see <http://www.gnu.org/licenses/>.
 *
 * Author: Giuseppe Piro <g.piro@poliba.it>
 */


#include "position-based-ho-manager.h"
#include "../../../device/NetworkNode.h"
#include "../../../device/UserEquipment.h"
#include "../../../device/ENodeB.h"
#include "../../../device/HeNodeB.h"
#include "../../../networkTopology/Sector.h"
#include "../../../componentManagers/NetworkManager.h"
#include "../../../flows/radio-bearer.h"
#include "../../../flows/application/Application.h"
#include "../../../flows/QoS/QoSParameters.h"
#include "../../../phy/lte-phy.h"
#include "../../../channel/LteChannel.h"

PositionBasedHoManager::PositionBasedHoManager()
{
    m_target = NULL;
}

PositionBasedHoManager::~PositionBasedHoManager()
{
    m_target = NULL;
}

bool
PositionBasedHoManager::CheckHandoverNeed (UserEquipment* ue)
{
    NetworkNode *targetNode = ue->GetTargetNode ();

    CartesianCoordinates *uePosition = ue->GetMobilityModel ()->GetAbsolutePosition ();
    CartesianCoordinates *targetPosition;

    targetPosition = targetNode->GetMobilityModel ()->GetAbsolutePosition ();
    double targetDistance = uePosition->GetDistance (targetPosition);

    /*
    if (targetDistance <= (ue->GetCell ()->GetRadius () * 0.8))
      {
      return false;
      }
    */

    std::vector<ENodeB*> *listOfNodes = NetworkManager::Init ()->GetENodeBContainer ();
    std::vector<ENodeB*>::iterator it;
    for (it = listOfNodes->begin (); it != listOfNodes->end (); it++)
    {
        if ((*it)->GetIDNetworkNode () != targetNode->GetIDNetworkNode () )
        {

            NetworkNode *probableNewTargetNode = (*it);


            double distance = probableNewTargetNode->GetMobilityModel ()->
                              GetAbsolutePosition ()->GetDistance (uePosition);

            if (distance < targetDistance)
            {
                if (NetworkManager::Init()->CheckHandoverPermissions(probableNewTargetNode,ue))
                {
                    targetDistance = distance;
                    targetNode = probableNewTargetNode;
                }
            }
        }
    }
    std::vector<HeNodeB*> *listOfNodes2 = NetworkManager::Init ()->GetHomeENodeBContainer();
    std::vector<HeNodeB*>::iterator it2;
    for (it2 = listOfNodes2->begin (); it2 != listOfNodes2->end (); it2++)
    {
        if ((*it2)->GetIDNetworkNode () != targetNode->GetIDNetworkNode () )
        {

            NetworkNode *probableNewTargetNode = (*it2);


            double distance = probableNewTargetNode->GetMobilityModel ()->
                              GetAbsolutePosition ()->GetDistance (uePosition);

            if (distance < targetDistance)
            {
                if (NetworkManager::Init()->CheckHandoverPermissions(probableNewTargetNode,ue))
                {
                    targetDistance = distance;
                    targetNode = probableNewTargetNode;
                }
            }
        }
    }

    if (ue->GetTargetNode ()->GetIDNetworkNode () != targetNode->GetIDNetworkNode ())
    {
        m_target = targetNode;
        return true;
    }
    else
    {
        return false;
    }
}

SectorBasedHoManager::SectorBasedHoManager()
{

}

SectorBasedHoManager:: ~SectorBasedHoManager()
{

}

bool
IsPositionOutsideSector (CartesianCoordinates *uePosition ,  NetworkNode *targetNode)
{
    bool bStat = false;
    Cell* pCurrentCell = dynamic_cast<Cell*>(targetNode);
    if(NULL == pCurrentCell)
    {
        return bStat;
    }
    Sector* pCurrentSector = NULL;
    CartesianCoordinates *targetPosition = targetNode->GetMobilityModel ()->GetAbsolutePosition ();
    double targetDistance = uePosition->GetDistance (targetPosition);
    if (targetDistance > pCurrentCell->GetRadius() )
    {
        //UE crossed Sector/Cell boundary required
        bStat = true;
    }
    else
    {
        pCurrentSector == dynamic_cast<Sector*>(targetNode);
        if (NULL != pCurrentSector)
        {
            // check if UE is in sector
            double azimuth = targetPosition->GetPolarAzimut (uePosition);
            if((azimuth > (pCurrentSector->GetCoveredAngleEnd() ))||
                    (azimuth < (pCurrentSector->GetCoveredAngleStart()))
              )
            {
                bStat = true;
            }
        }
        else
        {
            //not in Sector
            return false;
        }
    }
    return bStat;
}
bool IsResourceAvailable (  NetworkNode *oldTargetNode, NetworkNode * probableNewTargetNode,UserEquipment* ue)
{
    double totalBWReqd = 0;
    RrcEntity * oldEnbRrc = oldTargetNode->GetProtocolStack ()->GetRrcEntity ();
    for (std::vector<RadioBearer* >::iterator it = oldEnbRrc->GetRadioBearerContainer ()->begin();
            it != oldEnbRrc->GetRadioBearerContainer ()->end (); it++)
    {
        RadioBearer* bearer = (*it);
        if (bearer->GetDestination ()->GetIDNetworkNode () == ue->GetIDNetworkNode ())
        {
          totalBWReqd += bearer->GetApplication()->GetQoSParameters()->GetGBR();
        }
    }
    LteChannel *ch = probableNewTargetNode->GetPhy()->GetDlChannel();

    if ( ch->IsReservationPossible( totalBWReqd))
    {
        return true;
    }
    else
    {
        std::cout << "CAC failed for UE: " << ue->GetIDNetworkNode() << " ChannelId: " << ch->GetChannelId()  << " Max Bw(Mbps): "  << ch->GetBW() << " Curent BW Used(Mbps): " << ch->GetBWInUse() << " BW required(Mbps): " << totalBWReqd << std::endl;
        return false;
    }
}
bool
SectorBasedHoManager::CheckHandoverNeed (UserEquipment* ue)
{
    NetworkNode *targetNode = ue->GetTargetNode ();
    CartesianCoordinates *uePosition = ue->GetMobilityModel ()->GetAbsolutePosition ();
    bool bStat = IsPositionOutsideSector(uePosition, targetNode);
    if (false == bStat)
    {
        return bStat;
    }

    CartesianCoordinates *targetPosition = targetNode->GetMobilityModel ()->GetAbsolutePosition ();
    double targetDistance = uePosition->GetDistance (targetPosition);

    //get neighbour sectors/cell
    std::vector<ENodeB*> *listOfNodes = NetworkManager::Init ()->GetENodeBContainer ();
    std::vector<ENodeB*>::iterator it;
    for (it = listOfNodes->begin (); it != listOfNodes->end (); it++)
    {
        if ((*it)->GetIDNetworkNode () != targetNode->GetIDNetworkNode () )
        {
            NetworkNode *probableNewTargetNode = (*it);
            bool bStat = IsPositionOutsideSector(uePosition, probableNewTargetNode);
            if (true  == bStat)
            {
                continue;
            }

            if (NetworkManager::Init()->CheckHandoverPermissions(probableNewTargetNode,ue))
            {
              //check if resource is available in target eNB
              bStat = IsResourceAvailable(targetNode,probableNewTargetNode,ue );
              if (true == bStat)
              {
                targetNode = probableNewTargetNode;
              }
              else
              {  
                //need to drop call and make ue inactive
              }
            }
        }
    }
    if (ue->GetTargetNode ()->GetIDNetworkNode () != targetNode->GetIDNetworkNode ())
    {
        m_target = targetNode;
        return bStat = true;
    }
    else
    {
        return false;
    }
    return bStat;
}
